/*
 * Apple System Management Control (SMC) Tool 
 * Copyright (C) 2006 devnull 
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
/*
cc ./smc.c  -o smcutil -framework IOKit -framework CoreFoundation -Wno-four-char-constants -Wall -g -arch i386 
 */
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
//#include "OSTypes.h"
#include <IOKit/IOKitLib.h>
//#include <inttypes.h>
//#include <sys/types.h>
//#define CF_OPEN_SOURCE 1
//#include "OSTypes.h"
//#include "IOKitLib.h"

#include "smc.h"

io_connect_t conn;

UInt32 _strtoul(char *str, int size, int base) {
  UInt32 total = 0;
  int i;
  
  for (i = 0; i < size; i++) {
    if (base == 16)
      total += str[i] << (size - 1 - i) * 8;
    else
    /*total += (unsigned char) (str[i] << (size - 1 - i) * 8);*/
      total += (((UInt32)str[i] & 0xFF) << (size - 1 - i) * 8);
  }
  return total;
}

void _ultostr(char *str, UInt32 val) {
  str[0] = '\0';
  snprintf(str, 5, "%c%c%c%c",
           (unsigned int) val >> 24,
           (unsigned int) val >> 16,
           (unsigned int) val >> 8,
           (unsigned int) val);
}

float _strtof(char *str, int size, int e) {
  float total = 0;
  int i;
  
  for (i = 0; i < size; i++) {
    if (i == (size - 1)) {
      total += (str[i] & 0xff) >> e;
    } else {
      total += str[i] << (size - 1 - i) * (8 - e);
    }
  }
  return total;
}



kern_return_t SMCOpen(io_connect_t *conn) {
  kern_return_t result;
  mach_port_t   masterPort;
  io_iterator_t iterator;
  io_object_t   device;
  
  result = IOMasterPort(MACH_PORT_NULL, &masterPort);
  if (result != kIOReturnSuccess) {
    printf("Error: IOMasterPort() = %08x\n", result);
    return 1;
  }
  
  CFMutableDictionaryRef matchingDictionary = IOServiceMatching("AppleSMC");
  result = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
  if (result != kIOReturnSuccess) {
    printf("Error: IOServiceGetMatchingServices() = %08x\n", result);
    return 1;
  }
  
  device = IOIteratorNext(iterator);
  IOObjectRelease((io_object_t)iterator);
  if (device == 0) {
    printf("Error: no SMC found\n");
    return 1;
  }
  
  result = IOServiceOpen(device, mach_task_self(), 0, conn);
  IOObjectRelease(device);
  if (result != kIOReturnSuccess) {
    printf("Error: IOServiceOpen() = %08x\n", result);
    return 1;
  }
  
  return kIOReturnSuccess;
}

kern_return_t SMCClose(io_connect_t conn) {
  return IOServiceClose(conn);
}


kern_return_t SMCCall(int index, SMCKeyData_t *inputStructure, SMCKeyData_t *outputStructure) {
  size_t   structureInputSize;
  size_t   structureOutputSize;
  
  structureInputSize = sizeof(SMCKeyData_t);
  structureOutputSize = sizeof(SMCKeyData_t);
  
  return IOConnectCallStructMethod(
                                   conn,
                                   index,
                                   inputStructure,
                                   structureInputSize,
                                   outputStructure,
                                   &structureOutputSize
                                   );
}

kern_return_t SMCReadKey(UInt32Char_t key, SMCVal_t *val) {
  kern_return_t result;
  SMCKeyData_t  inputStructure;
  SMCKeyData_t  outputStructure;
  
  memset(&inputStructure, 0, sizeof(SMCKeyData_t));
  memset(&outputStructure, 0, sizeof(SMCKeyData_t));
  memset(val, 0, sizeof(SMCVal_t));
  
  inputStructure.key = _strtoul(key, 4, 16);
  snprintf(val->key, 5, "%s", key);
  inputStructure.data8 = SMC_CMD_READ_KEYINFO;
  
  result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure);
  if (result != kIOReturnSuccess)
    return result;
  
  val->dataSize = outputStructure.keyInfo.dataSize;
  _ultostr(val->dataType, outputStructure.keyInfo.dataType);
  inputStructure.keyInfo.dataSize = val->dataSize;
  inputStructure.data8 = SMC_CMD_READ_BYTES;
  
  result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure);
  if (result != kIOReturnSuccess) {
    return result;
  }
  
  memcpy(val->bytes, outputStructure.bytes, sizeof(outputStructure.bytes));
  return kIOReturnSuccess;
}

kern_return_t SMCWriteKey(SMCVal_t writeVal) {
  kern_return_t result;
  SMCKeyData_t  inputStructure;
  SMCKeyData_t  outputStructure;
  
  SMCVal_t      readVal;
  
  result = SMCReadKey(writeVal.key, &readVal);
  if (result != kIOReturnSuccess) {
    return result;
  }
  
  if (readVal.dataSize != writeVal.dataSize) {
    //        return kIOReturnError;
    writeVal.dataSize = readVal.dataSize;
  }
  
  memset(&inputStructure, 0, sizeof(SMCKeyData_t));
  memset(&outputStructure, 0, sizeof(SMCKeyData_t));
  
  inputStructure.key = _strtoul(writeVal.key, 4, 16);
  inputStructure.data8 = SMC_CMD_WRITE_BYTES;
  inputStructure.keyInfo.dataSize = writeVal.dataSize;
  memcpy(inputStructure.bytes, writeVal.bytes, sizeof(writeVal.bytes));
  
  result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure);
  if (result != kIOReturnSuccess) {
    return result;
  }
  
  return kIOReturnSuccess;
}
